package treeedit;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputMethodEvent;
import java.awt.event.InputMethodListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;

import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;


/**
 * 	Component intended for providing interactive clisp console.
 * 	Remeber that the path to clisp executable is taken from the
 * 	singleton configuration object using getClispPath method.
 * @author obiwan
 *
 */

public class JLispConsole extends JTextArea implements KeyListener
{
	private Process p;
	private InputStreamReader in;
	private OutputStreamWriter out;
	private InputStreamReader err;
	private ClispHandler handler;
	private boolean active;
	private String tempString;
	private ArrayList<String> history = new ArrayList<String>();
	int lastIndex;
	
	/** Construct the JLispConsole component */
	public JLispConsole()
	{
		setFont(new Font("Monospaced", Font.PLAIN, 12));
		setEditable(false);
		setFocusable(true);
		addKeyListener(this);
	//	setPreferredSize(new Dimension(1000,100));
	}

	/** 
	 * 	This method runs the specyfied filename contaning clisp code using
	 * 	the interpreter.
	 * 
	 * 	@param filename the specyfied file
	 *  @throws IOException thrown when there is a problem with running the interpreter
	 *
	 */
	public void runFromFile ( String filename ) throws IOException
	{
		setText("");
		active = false;
		tempString = "";
		history = new ArrayList<String> ();
		lastIndex=0;
		String[] command = new String[2];
		command[0] = Configuration.getInstance().getClispPath();
		command[1] = " " + filename;
		
		System.out.println(command[0]);

		System.out.println(command[1]);
		
		
		p = Runtime.getRuntime().exec(command);	
		
		out = new OutputStreamWriter(p.getOutputStream());
		in = new InputStreamReader(p.getInputStream());
		err = new InputStreamReader(p.getErrorStream());
		handler = new ClispHandler();
		handler.start();
		active = true;
	}
	
	
	/**
	 * Runs the clisp interpreter with the given code
	 * 
	 * @param text the lisp code
	 * @throws IOException 
	 */
	public void runFromString(String text) throws IOException
	{
		File file = File.createTempFile("lisp", null);
		FileWriter fw = new FileWriter(file);
		
		fw.write(text);
		fw.close();
		
		runFromFile(file.getAbsolutePath());
	}
	
	/** 
	 * 	Kills clisp
	 *
	 */
	public void kill ()
	{
		if(p == null) return ;
		p.destroy();
		handler.interrupt();
		active = false;
	}
		
	/** 
	 * Simulates shell arrow up and down reaction
	 *
	 */
	public void keyPressed(KeyEvent k) 
	{
		if(history.size() == 0) return;
		
		if(k.getKeyCode() == 38)
		{//Arrow up
			if(lastIndex < 0 ) return;
			setText(getText().substring(0, getText().length() - tempString.length()));
			tempString = history.get(lastIndex);
			setText(getText() + tempString);
			lastIndex--;
		}
		else if(k.getKeyCode() == 40)
		{//Arrow down
			
			if(lastIndex >= history.size() -1) return ;
			setText(getText().substring(0, getText().length() - tempString.length()));
			tempString = history.get(lastIndex+1);
			setText(getText() + tempString);
			lastIndex++;
		}
	}
	
	/** Empty */
	public void keyReleased(KeyEvent e) {}

	/** Forwards keys to the propertly run clisp application */
	public void keyTyped(KeyEvent k) 
	{
		k.consume();
		if(!active) return;
		
		int keyCode = Integer.valueOf(k.getKeyChar());
		String str = getText();
		if(keyCode == 127) return; //Del key
		if(keyCode == 8) 
		{ //Backspace
			if(!tempString.equals(""))
			{
				setText(str.substring(0,str.length() -1));
				tempString = tempString.substring(0,tempString.length() -1);
			}
			return;
		}
		
		//Everything in order and appending char
		
		tempString += k.getKeyChar();
		//System.out.println(keyCode);
		setText(getText() + k.getKeyChar());
		
		if(k.getKeyChar() == '\n')
		{
			try {
				out.write(tempString);
				out.flush();
				history.add(tempString);
				lastIndex = history.size() - 1;
				tempString = "";
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
		}
	
	}
	
	private class ClispHandler extends Thread
	{

		@Override
		public void run() 
		{
			char c[] = new char[100];
			try {
				while(true)
				{
//					System.out.println("before");
					
				//	System.out.println("f");
					
					while(in.ready())
					{
//						System.out.println("in loop");
						int howMany = in.read(c);
						setText(getText() + new String(c,0,howMany));
						//System.out.print(c[0]);
						tempString = "";
					}
					while(err.ready())
					{
//						System.out.println("in loop");
						int howMany = err.read(c);
						setText(getText() + new String(c,0,howMany));
						//System.out.print(c[0]);
						tempString = "";
					}
					
					sleep(300);
					//Testing if the process ended
					try
					{
						p.exitValue();
						while(in.ready())
						{
//							System.out.println("in loop");
							int howMany = in.read(c);
							setText(getText() + new String(c,0,howMany));
							//System.out.print(c[0]);
							tempString = "";
						}
						while(err.ready())
						{
//							System.out.println("in loop");
							int howMany = err.read(c);
							setText(getText() + new String(c,0,howMany));
							//System.out.print(c[0]);
							tempString = "";
						}
						System.out.println("ended");
						active = false;
						return ;
					}
					catch(Exception e)
					{
						//e.printStackTrace();
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			} catch ( InterruptedException e)
			{
				//System.out.println("Interrupted");
			}
			
		}
		
		
		
	}
	
}
